home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 2.toast / pc / sample code / printing / lw8_hosesample / hoseirda.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-06-23  |  21.3 KB  |  767 lines

  1. /*
  2.     File:        HoseIrda.c
  3.  
  4.     Contains:    Sample code to demonstrate how a LaserWriter communication hose 
  5.                 plugin can be written. An Irda (Infrared) hose is used here.
  6.  
  7.     Written by: Chorng-Shyan Lin and Ingrid Kelly    
  8.  
  9.     Copyright:    Copyright © 1999 by Apple Computer, Inc., All Rights Reserved.
  10.  
  11.                 You may incorporate this Apple sample source code into your program(s) without
  12.                 restriction. This Apple sample source code has been provided "AS IS" and the
  13.                 responsibility for its operation is yours. You are not permitted to redistribute
  14.                 this Apple sample source code as "Apple sample source code" after having made
  15.                 changes. If you're going to re-distribute the source, we require that you make
  16.                 it clear in the source that the code was descended from Apple sample source
  17.                 code, but that you've made changes.
  18.  
  19.     Change History (most recent first):
  20.                 7/26/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
  21.                 5/5/99        lin                changed the way cntl-D is sent at close time,
  22.                                             longer time-out
  23.                 
  24.  
  25. */
  26.  
  27. #include <CommResources.h>
  28. #include <CTBUtilities.h>
  29. #include <Connections.h>
  30.  
  31. #include "Hose.h"
  32. #include "HoseIrda.h"
  33. #include "PSWriterErr.h"                    
  34.  
  35. #define LockHint(collection, tag, id)    SetCollectionItemInfo((collection), (tag), (id), kCollectionLockMask, kCollectionLockMask)        
  36.  
  37. /*    The following two macros are intended to make it easier to
  38.     declare routines that will be invoked by the MixedMode Mgr.
  39.     DeclareProcPtr() will create a routine descriptor with the
  40.     name g_'proc' if we are compiling in an environment that
  41.     needs routine descriptors. If we are in the old 68K environment
  42.     then this macro does nothing. MakeProcPtr creates a reference to
  43.     the function pointer 'proc'. If we are using routine descriptors
  44.     then the reference is the address of the descriptor created by
  45.     DeclareProcPtr(). If we are in a 68K world then the reference
  46.     is just the proc pointer itself.
  47. */
  48. #if TARGET_RT_MAC_CFM
  49.  
  50. #define DeclareProcPtr(proc, upp)    \
  51.         RoutineDescriptor g_##proc = BUILD_ROUTINE_DESCRIPTOR(upp, proc);
  52. #define ExternalProcPtr(proc)    \
  53.         extern RoutineDescriptor g_##proc;
  54. #define MakeProcPtr(proc, procType)    \
  55.         ((procType)&g_##proc)
  56.         
  57. #else
  58.  
  59. #define DeclareProcPtr(proc, upp)
  60. #define ExternalProcPtr(proc)
  61. #define MakeProcPtr(proc, procType)    \
  62.         ((procType)proc)
  63.         
  64. #endif
  65.  
  66. /*** Constants ***/
  67. #define kClosingTimeOut 3600
  68.  
  69. /*** Variable Types ***/
  70. struct IrdaHoseRec {
  71.     ConnHandle                             connectionH;
  72.     ConnState                            connectionState;
  73.     MemQElemPtr                            writeMemQElemPtr;
  74.     MemQElemPtr                            readMemQElemPtr;
  75.     BufCallbacks                        callbacks;
  76.     Boolean                                eojFired;
  77.     Boolean                                needToDumpDataOut;
  78.     Boolean                                needToRequestStatus;
  79.     OSErr                                openResult;
  80.     Boolean                                needCloseEoj;    
  81.     long                                startCloseTime;    
  82. };
  83. typedef struct IrdaHoseRec            IrdaHoseRec;
  84. typedef IrdaHoseRec*                IrdaHoseRecPtr;
  85.  
  86.  
  87. /*** Prototypes ***/
  88. OSErr InitLib(CFragInitBlockPtr initBlkPtr);                                                        
  89. void TerminateLib(void);
  90.  
  91. // hose API
  92. static OSStatus closeIrdaHose(void *refcon);
  93. static OSStatus outIrdaHose(void *refcon, MemQElemPtr memElem);
  94. static OSStatus inIrdaHose(void *refcon, MemQElemPtr memElem);
  95. static OSStatus idleIrdaHose(void *refcon);
  96. static OSStatus statusIrdaHose(void *refcon, StringPtr statusStr);                
  97. static OSStatus disposeIrdaHose(void *refcon);                                
  98. static ConnState connStateIrdaHose(void *refcon);
  99.  
  100. // callback from CTB
  101. static  void commOpenCompletion(ConnHandle connectionH);
  102. static  void commWriteCompletion(ConnHandle connectionH);
  103. static  void commReadCompletion(ConnHandle connectionH);
  104. static  void commStatusCompletion(ConnHandle connectionH);
  105.  
  106. static  void closingEojSentCompletion(ConnHandle connectionH);
  107. static  void closingEojReceivedCompletion(ConnHandle connectionH);
  108.  
  109. // utilities
  110. static OSStatus requestStatus(IrdaHoseRecPtr irdaHoseP);
  111. static OSStatus cmWritePending(ConnHandle connectionH, Boolean* writePendingP);
  112.  
  113. static OSErr translateErr(OSErr err);
  114. static void StripEOJs(unsigned char* buffer, SInt32* nBytesP);    // lin016
  115. static OSErr UnLockHint(Collection collection, CollectionTag tag, long id);
  116.  
  117. /***Globals**/
  118. unsigned char    gInCntlD = 0x04;
  119. unsigned char    gOutCntlD = 0x04;
  120. unsigned char    gCntlT = 0x14;
  121. long    gInOne = 1;
  122. long    gOutOne = 1;
  123. char *gIrDALaserconfigStr = "myClass: LaserPrinter myAttr: IrDA:IrLMP:LsapSel peerClass: IrLPT peerAttr: IrDA:IrLMP:LsapSel\0";
  124.  
  125. DeclareProcPtr(commOpenCompletion, uppConnectionCompletionProcInfo)
  126. DeclareProcPtr(commWriteCompletion, uppConnectionCompletionProcInfo)
  127. DeclareProcPtr(commReadCompletion, uppConnectionCompletionProcInfo)
  128. DeclareProcPtr(commStatusCompletion, uppConnectionCompletionProcInfo)
  129.  
  130. DeclareProcPtr(closingEojSentCompletion, uppConnectionCompletionProcInfo)
  131. DeclareProcPtr(closingEojReceivedCompletion, uppConnectionCompletionProcInfo)
  132.  
  133. /*** Code ***/
  134. OSErr InitLib(CFragInitBlockPtr /* initBlkPtr */)
  135. {
  136.     return noErr;
  137. }
  138.  
  139. void TerminateLib(void)
  140. {
  141.     // any clean up if needed
  142.     // do nothing in this case
  143. }                                                        
  144.  
  145.  
  146. #pragma mark -
  147. #pragma export on
  148. OSStatus hoseOpen(HoseInfo *hoseInfo, BufCallbacks *callbacks, Collection hints, Handle /* papaH */)        
  149. /*    
  150. Fill out 'hose' with information that describes the IrDA hose
  151. and Open connection to IrDA printer.
  152. */
  153. {
  154. OSStatus err = noErr;
  155. short irdaProcID = -1;    // -1 is invalid ProcID
  156. ConnHandle connectionH = 0;
  157. CMBufferSizes irDaBufSizes;
  158. IrdaHoseRecPtr irdaHoseP = 0;
  159. Boolean eighthBit = false;                    
  160. Boolean transparentBit = false;                    
  161. short i;
  162.  
  163.     // fill out the proc pointers to our hose
  164.     hoseInfo->out = outIrdaHose;        
  165.     hoseInfo->in = inIrdaHose;                
  166.     hoseInfo->idle = idleIrdaHose;            
  167.     hoseInfo->close = closeIrdaHose;        
  168.     hoseInfo->connState = connStateIrdaHose;        
  169.     hoseInfo->status = statusIrdaHose;    
  170.     hoseInfo->dispose = disposeIrdaHose;
  171.     // buffer info
  172.     hoseInfo->bufSize = kPrinterHoseBufSize;            
  173.     hoseInfo->minBufs = 4;                
  174.     hoseInfo->maxBufs = kPrinterHoseMaxBufs;                
  175.     // add necessary hints
  176.     err = UnLockHint(hints, kHintEighthBitTag, kHintDataFormatId);
  177.     err = AddCollectionItem(hints, kHintEighthBitTag, kHintDataFormatId, sizeof(eighthBit), &eighthBit);
  178.     if(!err) err = LockHint(hints, kHintEighthBitTag, kHintDataFormatId);                
  179.  
  180.     if(!err) err = UnLockHint(hints, kHintTransparentChannelTag, kHintTransparentChannelId);
  181.     if(!err) err = AddCollectionItem(hints, kHintTransparentChannelTag, kHintTransparentChannelId, 
  182.                                         sizeof(transparentBit), &transparentBit);
  183.     if(!err) err = LockHint(hints, kHintTransparentChannelTag, kHintTransparentChannelId);    
  184.     
  185.     // allocate private storage for the hose
  186.     if(!err) irdaHoseP = (IrdaHoseRecPtr)NewPtrClear(sizeof(IrdaHoseRec));                
  187.     if(irdaHoseP){
  188.         hoseInfo->refcon = (void *)irdaHoseP;                
  189.         irdaHoseP->connectionH = 0;
  190.         irdaHoseP->callbacks = *callbacks;
  191.         if(InitCRM != 0){    // if there is Communication Toolbox, initialize it
  192.             err = InitCRM();
  193.             if(!err)
  194.                 err = InitCTBUtilities();
  195.             if(!err)
  196.                 err = InitCM();
  197.             if(!err){
  198.                 irdaProcID = CMGetProcID("\pIrDA Tool");    // name of IrDA tool
  199.                 if(irdaProcID != -1){
  200.                     for(i = cmDataIn; i <= cmRsrvOut ; i++)
  201.                         irDaBufSizes[i] = 0;    // let IrDA tool decide buffer sizes
  202.                     // make a new connection record
  203.                     irdaHoseP->connectionH = connectionH = CMNew(irdaProcID, cmData | cmNoMenus | cmQuiet, irDaBufSizes, (long)irdaHoseP, 0);
  204.                     if(connectionH){
  205.                         // configure the connection record
  206.                         err = CMSetConfig (connectionH, gIrDALaserconfigStr);
  207.                         if(err == cmNoErr){
  208.                             irdaHoseP->connectionState = kConnOpening;    
  209.                             // try to open a connection async.                        
  210.                             err = CMOpen(connectionH, true, MakeProcPtr(commOpenCompletion, ConnectionCompletionUPP), -1);    
  211.                             if(err != cmNoErr)
  212.                                 irdaHoseP->connectionState = kConnClosed;
  213.                         }else{
  214.                             err = irdaHoseP->openResult = errIrdaUnknownOpenErr;
  215.                             /* CTB does not provide a good error code */                
  216.                         }
  217.                     }else{
  218.                         err = irdaHoseP->openResult = errIrdaUnknownOpenErr;    
  219.                         /* CTB does not provide a good error code, possibly out of memory */                
  220.                     }
  221.                 }else{
  222.                     err = irdaHoseP->openResult = errIrdaToolNotFoundErr;    // no comm. tool for irDA
  223.                 }
  224.             }
  225.         }else{
  226.             err = irdaHoseP->openResult = errHoseCantBeUsed;    // CTB not available
  227.         }
  228.     }else{
  229.         err = irdaHoseP->openResult = memFullErr;    // out of memory
  230.     }
  231.     if(err){    // clean up
  232.         if(connectionH)
  233.             CMDispose(connectionH);
  234.         if(irdaHoseP){
  235.             DisposePtr((Ptr)irdaHoseP);
  236.             hoseInfo->refcon = 0;
  237.         }
  238.     }
  239.     return (translateErr(err));
  240. }
  241. #pragma export off
  242.  
  243.  
  244. static OSStatus statusIrdaHose(void *refcon, StringPtr /* statusStr */)
  245. {
  246. IrdaHoseRecPtr irdaHoseP;
  247. OSStatus err = noErr;
  248.  
  249.     if((irdaHoseP = (IrdaHoseRecPtr)refcon) != 0){
  250.         irdaHoseP->needToRequestStatus = true;
  251.         // request status from printer
  252.         err = requestStatus(irdaHoseP);
  253.     }
  254.     if(err){
  255.         err = statusErr;
  256.         if(irdaHoseP && irdaHoseP->openResult)
  257.             err = irdaHoseP->openResult;
  258.     }
  259.     return (err);
  260. }
  261.  
  262. static OSStatus closeIrdaHose(void *refcon)
  263. {
  264. OSStatus err = noErr;
  265. IrdaHoseRecPtr irdaHoseP;
  266. ConnHandle connectionH;
  267. Boolean writePending;
  268.     
  269.     if((irdaHoseP = (IrdaHoseRecPtr)refcon) != 0){
  270.         irdaHoseP->connectionState = kConnClosing;
  271.         if((connectionH = irdaHoseP->connectionH) != 0){
  272.             // depending on the type of hose and printer, we may not want to
  273.             // report "closed" until the printer is done and ready for the next job
  274.             // in this particular hose, we wait for the printer to respond to our extra eoj
  275.             irdaHoseP->startCloseTime = TickCount();
  276.             irdaHoseP->needCloseEoj = true;
  277.             err = cmWritePending(connectionH, &writePending);
  278.             if(!err && !writePending){
  279.                 irdaHoseP->needCloseEoj = false;
  280.                 gOutOne = 1;
  281.                 err = CMWrite(connectionH,
  282.                              &gOutCntlD,
  283.                              &gOutOne,
  284.                              cmData,
  285.                              true,
  286.                              MakeProcPtr(closingEojSentCompletion, ConnectionCompletionUPP),
  287.                              -1,
  288.                              0);
  289.                          
  290.             }
  291.         }
  292.     }
  293.     return (noErr);
  294. }
  295.  
  296. /*
  297.     routine to write data stream out, async.
  298. */
  299. static OSStatus outIrdaHose(void *refcon, MemQElemPtr memElem)
  300. {
  301. OSStatus err = paramErr;                                        
  302. IrdaHoseRecPtr irdaHoseP;
  303. ConnHandle connectionH;
  304. Boolean writePending;
  305.  
  306.     if((irdaHoseP = (IrdaHoseRecPtr)refcon) != 0){
  307.         if(irdaHoseP->connectionState == kConnClosing){
  308.             err = noErr;    // ignore it if we are already closing
  309.         }else{
  310.             if(irdaHoseP->connectionState == kConnOpen){    // if the hose is open
  311.                 irdaHoseP->writeMemQElemPtr = memElem;
  312.                 irdaHoseP->eojFired = false;
  313.                 if((connectionH = irdaHoseP->connectionH) != 0){
  314.                     // CTB does not allow multiple writes, may have to postpone it
  315.                     err = cmWritePending(connectionH, &writePending);
  316.                     if(!err){
  317.                         irdaHoseP->needToDumpDataOut = writePending ? true : false;
  318.                         if(!writePending){
  319.                             err = CMWrite(connectionH,
  320.                                          memElem->buf,
  321.                                          &(memElem->nBytes),
  322.                                          cmData,
  323.                                          true,
  324.                                          MakeProcPtr(commWriteCompletion, ConnectionCompletionUPP),
  325.                                          -1,
  326.                                          0);
  327.                             if(err){
  328.                                 err = errIrdaDisconnectedErr;    
  329.                                 err = translateErr(err);                    
  330.                             }
  331.                         }
  332.                     }
  333.                 }
  334.             }else{
  335.                 err = PAPNoPrinter;
  336.             }
  337.         }
  338.     }
  339.     if(err){
  340.         if(irdaHoseP && irdaHoseP->openResult)
  341.             err = irdaHoseP->openResult;
  342.     }
  343.     return err;                                                        
  344. }
  345.  
  346.  
  347. /*
  348.     routine to read data stream in, async.
  349. */
  350. static OSStatus inIrdaHose(void *refcon, MemQElemPtr memElem)
  351. {
  352. OSStatus err = paramErr;                                        
  353. IrdaHoseRecPtr irdaHoseP;
  354. ConnHandle connectionH;
  355. long numOfBytes;
  356. CMBufferSizes cmBufSizes;
  357. CMStatFlags cmFlags;
  358.  
  359.     memElem->nBytes = 0;
  360.     if((irdaHoseP = (IrdaHoseRecPtr)refcon) != 0){
  361.         if(irdaHoseP->connectionState == kConnClosing){
  362.             err = noErr;    // ignore it if we are already closing
  363.         }else{
  364.             if(irdaHoseP->connectionState == kConnOpen){    // if the hose is open
  365.                 irdaHoseP->readMemQElemPtr = memElem;
  366.                 if((connectionH = irdaHoseP->connectionH) != 0){
  367.                     err = CMStatus(connectionH,
  368.                                     cmBufSizes,
  369.                                     &cmFlags);
  370.                     if(!err){
  371.                         numOfBytes = cmBufSizes[cmDataIn];    // number of bytes available
  372.                         if(numOfBytes > memElem->maxBytes)
  373.                             numOfBytes = memElem->maxBytes;
  374.                         if(numOfBytes <= 0)
  375.                             numOfBytes = 1;    // try to read at least one byte
  376.                         err = CMRead(connectionH,
  377.                                      memElem->buf,
  378.                                      &numOfBytes,
  379.                                      cmData,
  380.                                      true,
  381.                                      MakeProcPtr(commReadCompletion, ConnectionCompletionUPP),
  382.                                      -1,
  383.                                      0);
  384.                         if(err){
  385.                             err = errIrdaDisconnectedErr;    
  386.                             err = translateErr(err);                    
  387.                         }
  388.                     }
  389.                 }
  390.             }else{
  391.                 err = PAPNoPrinter;
  392.             }
  393.         }
  394.     }
  395.     if(err){
  396.         if(irdaHoseP && irdaHoseP->openResult)
  397.             err = irdaHoseP->openResult;
  398.     }
  399.     return err;                                                        
  400. }
  401.  
  402.  
  403. static OSStatus idleIrdaHose(void *refcon)
  404. {
  405. IrdaHoseRecPtr irdaHoseP;
  406. ConnHandle connectionH;
  407. OSStatus err = noErr;
  408.  
  409.     if((irdaHoseP = (IrdaHoseRecPtr)refcon) != 0){
  410.         if((connectionH = irdaHoseP->connectionH) != 0){
  411.             CMIdle(connectionH);
  412.             // take this chance to request status if necessary
  413.             requestStatus(irdaHoseP);
  414.         }
  415.         err = irdaHoseP->openResult ? irdaHoseP->openResult : noErr;
  416.     }
  417.     return(err);
  418. }
  419.  
  420.  
  421. static OSStatus disposeIrdaHose(void *refcon)
  422. {
  423. OSStatus err = noErr;
  424. IrdaHoseRecPtr irdaHoseP;
  425. ConnHandle connectionH;
  426.  
  427.     if((irdaHoseP = (IrdaHoseRecPtr)refcon) != 0){
  428.         if((connectionH = irdaHoseP->connectionH) != 0){    
  429.             // close connection
  430.             err = CMClose(connectionH, false, 0, -1, true);
  431.             // dispose connection record
  432.             CMDispose(connectionH);
  433.         }    
  434.         // dispose private storage
  435.         DisposePtr((Ptr)irdaHoseP);
  436.     }
  437.     return noErr;
  438. }
  439.  
  440.  
  441. /*    Return the current state of the hose
  442. */
  443. static ConnState connStateIrdaHose(void *refcon)
  444. {
  445. IrdaHoseRecPtr irdaHoseP;
  446. ConnHandle connectionH;
  447. ConnState connState = kConnClosed;
  448. OSStatus err = noErr;
  449. Boolean writePending;
  450.  
  451.     if((irdaHoseP = (IrdaHoseRecPtr)refcon) != 0){
  452.         if(irdaHoseP->connectionState == kConnClosing){    // if we are closing down the hose
  453.             if((TickCount() - irdaHoseP->startCloseTime) > kClosingTimeOut){    // test time-out
  454.                 irdaHoseP->connectionState = kConnClosed;
  455.             }else{
  456.                 // write an extra eoj to printer as part of closing process
  457.                 connectionH = irdaHoseP->connectionH;
  458.                 if(irdaHoseP->needCloseEoj && connectionH){
  459.                     err = cmWritePending(connectionH, &writePending);
  460.                     if(!err && !writePending){
  461.                         gOutOne = 1;
  462.                         err = CMWrite(connectionH,
  463.                                      &gOutCntlD,
  464.                                      &gOutOne,
  465.                                      cmData,
  466.                                      true,
  467.                                      MakeProcPtr(closingEojSentCompletion, ConnectionCompletionUPP),
  468.                                      -1,
  469.                                      0);
  470.                         irdaHoseP->needCloseEoj = false;
  471.                     }
  472.                 }
  473.             }
  474.         }
  475.         connState = irdaHoseP->connectionState;
  476.     }
  477.     return connState;
  478. }
  479.  
  480.  
  481. /*
  482.     This call-back routine is called by CTB when our open request completes
  483. */
  484. static void commOpenCompletion(ConnHandle connectionH)
  485. {
  486. IrdaHoseRecPtr irdaHoseP;
  487.  
  488.     if(connectionH){
  489.         irdaHoseP = (IrdaHoseRecPtr)(*connectionH)->refCon;
  490.         if(irdaHoseP){
  491.             irdaHoseP->connectionState = (*connectionH)->errCode ? kConnClosed : kConnOpen;
  492.             irdaHoseP->openResult = (*connectionH)->errCode ? PAPNoPrinter : noErr;
  493.         }
  494.     }
  495. }
  496.  
  497. static  void closingEojSentCompletion(ConnHandle connectionH)
  498. {
  499. IrdaHoseRecPtr irdaHoseP;
  500. OSStatus err;
  501.  
  502.     if(connectionH){
  503.         if((irdaHoseP = (IrdaHoseRecPtr)(*connectionH)->refCon) != 0){
  504.             err = (*connectionH)->errCode;
  505.             if(err == noErr){
  506.                 gInOne = 1;
  507.                 err = CMRead(connectionH,
  508.                              &gInCntlD,
  509.                              &gInOne,
  510.                              cmData,
  511.                              true,
  512.                              MakeProcPtr(closingEojReceivedCompletion, ConnectionCompletionUPP),
  513.                              -1,
  514.                              0);
  515.             }
  516.         }
  517.     }    
  518. }
  519.  
  520. static  void closingEojReceivedCompletion(ConnHandle connectionH)
  521. {
  522. IrdaHoseRecPtr irdaHoseP;
  523.  
  524.     /* we can further test if the received data contain eoj from the printer. 
  525.        we don't do this here b/c we seem to have posponed the closing report long enough
  526.        already and also it could be an overkill to wait for the eoj.
  527.     */
  528.     if(connectionH){
  529.         if((irdaHoseP = (IrdaHoseRecPtr)(*connectionH)->refCon) != 0){
  530.             irdaHoseP->connectionState = kConnClosed;
  531.         }
  532.     }
  533. }
  534.  
  535. static void commWriteCompletion(ConnHandle connectionH)
  536. {
  537. IrdaHoseRecPtr irdaHoseP;
  538. MemQElemPtr writeMemQElemPtr;
  539. OSStatus err;
  540.  
  541.     if(connectionH){
  542.         irdaHoseP = (IrdaHoseRecPtr)(*connectionH)->refCon;
  543.         if(irdaHoseP){
  544.             writeMemQElemPtr = irdaHoseP->writeMemQElemPtr;
  545.             err = (*connectionH)->errCode;
  546.             if(err){
  547.                 err = errIrdaDisconnectedErr;    
  548.                 err = translateErr(err);                    
  549.             }
  550.  
  551.             // if we have sent an eoj as part of the previous write request
  552.             if(irdaHoseP->eojFired){    
  553.                 requestStatus(irdaHoseP);    // give status request a chance
  554.                 
  555.                 irdaHoseP->eojFired = false;
  556.                 // notify client of completion
  557.                 if(err && (irdaHoseP->connectionState == kConnClosing))
  558.                     err = noErr;
  559.                 if(irdaHoseP->callbacks.finishedWrite)
  560.                     (irdaHoseP->callbacks.finishedWrite)(writeMemQElemPtr, err);
  561.             }else{
  562.                 if(!err && writeMemQElemPtr->eoj){    // need to send an eoj
  563.                     irdaHoseP->eojFired = true;
  564.                     gOutOne = 1;
  565.                     err = CMWrite(connectionH,
  566.                                  &gOutCntlD,
  567.                                  &gOutOne,
  568.                                  cmData,
  569.                                  true,
  570.                                  MakeProcPtr(commWriteCompletion, ConnectionCompletionUPP),
  571.                                  -1,
  572.                                  0);
  573.                     // notify client of error
  574.                     if(err){
  575.                         irdaHoseP->eojFired = false;
  576.                         if(err && (irdaHoseP->connectionState == kConnClosing))
  577.                             err = noErr;
  578.                         if(irdaHoseP->callbacks.finishedWrite)
  579.                             (irdaHoseP->callbacks.finishedWrite)(writeMemQElemPtr, err);
  580.                     }
  581.                 }else{
  582.                     requestStatus(irdaHoseP);    // give status request a chance
  583.                     if(err && (irdaHoseP->connectionState == kConnClosing))
  584.                         err = noErr;
  585.                     // notify client of completion
  586.                     if(irdaHoseP->callbacks.finishedWrite)
  587.                         (irdaHoseP->callbacks.finishedWrite)(writeMemQElemPtr, err);
  588.                 }
  589.             }
  590.         }
  591.     }
  592. }
  593.  
  594. static void commReadCompletion(ConnHandle connectionH)
  595. {
  596. IrdaHoseRecPtr irdaHoseP;
  597. MemQElemPtr readMemQElemPtr;
  598. OSStatus err;
  599.  
  600.     if(connectionH){
  601.         err = (*connectionH)->errCode;
  602.         if(err){
  603.             err = errIrdaDisconnectedErr;                
  604.             err = translateErr(err);                    
  605.         }
  606.         irdaHoseP = (IrdaHoseRecPtr)(*connectionH)->refCon;
  607.         if(irdaHoseP){
  608.             if(err && (irdaHoseP->connectionState == kConnClosing))
  609.                 err = noErr;
  610.             readMemQElemPtr = irdaHoseP->readMemQElemPtr;
  611.             if(readMemQElemPtr){
  612.                 readMemQElemPtr->nBytes = *(SInt32*)(&((*connectionH)->asyncCount[cmDataIn]));
  613.                 StripEOJs(readMemQElemPtr->buf, &readMemQElemPtr->nBytes);    
  614.                 if(irdaHoseP->callbacks.finishedRead)
  615.                     (irdaHoseP->callbacks.finishedRead)(readMemQElemPtr, err);
  616.             }
  617.         }
  618.     }
  619. }
  620.  
  621.  
  622. static void commStatusCompletion(ConnHandle connectionH)
  623. {
  624. IrdaHoseRecPtr irdaHoseP;
  625. OSStatus err;
  626.  
  627.     if(connectionH){
  628.         irdaHoseP = (IrdaHoseRecPtr)(*connectionH)->refCon;
  629.         // if there is data waiting to be sent
  630.         if(irdaHoseP && (irdaHoseP->needToDumpDataOut)){
  631.             irdaHoseP->needToDumpDataOut = false;
  632.             err = CMWrite(connectionH,
  633.                          irdaHoseP->writeMemQElemPtr->buf,
  634.                          &(irdaHoseP->writeMemQElemPtr->nBytes),
  635.                          cmData,
  636.                          true,
  637.                          MakeProcPtr(commWriteCompletion, ConnectionCompletionUPP),
  638.                          -1,
  639.                          0);
  640.         }    
  641.     }
  642. }
  643.  
  644.  
  645. static OSStatus requestStatus(IrdaHoseRecPtr irdaHoseP)
  646. {
  647. OSStatus err = noErr;
  648. ConnHandle connectionH;
  649. Boolean writePending;
  650.  
  651.     if((irdaHoseP->connectionState == kConnOpen) && (irdaHoseP->needToRequestStatus)){
  652.         if((connectionH = irdaHoseP->connectionH) != 0){
  653.             err = cmWritePending(connectionH, &writePending);
  654.             
  655.             // CTB does NOT allow multiple writes
  656.             if(!err && !writePending){
  657.                 irdaHoseP->needToRequestStatus = false;
  658.                 gOutOne = 1;
  659.                 // request status from printer
  660.                 err = CMWrite(connectionH,
  661.                              &gCntlT,
  662.                              &gOutOne,
  663.                              cmData,
  664.                              true,
  665.                              MakeProcPtr(commStatusCompletion, ConnectionCompletionUPP),
  666.                              -1,
  667.                              0);
  668.             }
  669.         }    
  670.     }
  671.     return (err);
  672. }
  673.  
  674. static OSStatus cmWritePending(ConnHandle connectionH, Boolean* writePendingP)
  675. {
  676. OSStatus err;
  677. CMBufferSizes cmBufSizes;
  678. CMStatFlags cmFlags;
  679.  
  680.     *writePendingP = false;
  681.     err = CMStatus(connectionH,
  682.                     cmBufSizes,
  683.                     &cmFlags);
  684.     // CTB does NOT allow multiple writes
  685.     if(!err && (cmFlags & cmStatusDWPend))
  686.         *writePendingP = true;
  687.     return (err);
  688. }
  689.  
  690.  
  691. /* 
  692. translate connection Mgr. errors to something meaningful
  693. */
  694. static OSErr translateErr(OSErr err)
  695. {
  696. OSErr newErr = err;
  697.  
  698.     switch(err){
  699.         case cmNotOpen:
  700.         case readErr:                    // readErr and writErr should be mapped to 'errIrdaDisconnectedErr' when
  701.         case writErr:                    // we can supply better error reporting for that error. 
  702.         case cmNotClosed:                // This should be 'errHoseInUse' when we can supply better error reporting. 
  703.         case errIrdaDisconnectedErr:     // also make sure that this error is mapped  
  704.             newErr = PAPNoPrinter;
  705.         break;
  706.         
  707.         case errIrdaUnknownOpenErr:                      
  708.             newErr = errUnableToDoNewIrdaConnection;
  709.         break;
  710.         
  711.         case cmNoTools:
  712.             newErr = errIrdaToolNotFoundErr;
  713.         break;
  714.         
  715.         case cmGenericError:
  716.         case cmRejected:
  717.         case cmFailed:
  718.         case cmTimeOut:
  719.         case cmNoRequestPending:
  720.         case cmNotSupported:
  721.         case cmUserCancel:
  722.         case cmUnknownError:
  723.             newErr = errGenericComponentErr;
  724.         break;
  725.     }
  726.     return newErr;
  727. }
  728.  
  729.  
  730.  
  731. static void StripEOJs(unsigned char* buffer, SInt32* nBytesP)        
  732. {
  733.     unsigned char *src, *dst;
  734.     SInt32    count;
  735.  
  736.     src = dst = buffer;
  737.     count = *nBytesP + 1;    // +1 for pre-decrement
  738.  
  739.     while (--count)
  740.     {
  741.         if ((*dst++ = *src++) == '\x04')
  742.         {
  743.             dst--;            // Opps, we hit a control-D, backup a character & decrement num bytes
  744.             (*nBytesP)--;
  745.         }
  746.     }
  747. }
  748.  
  749.  
  750. static OSErr UnLockHint(Collection collection, CollectionTag tag, long id)
  751. /*     If the hint is locked UnLockHint() will unlock it, if
  752.     it is unlocked UnLockHint() will do nothing.
  753. */
  754. {
  755.     OSErr err = noErr;
  756.     long attributes;
  757.      
  758.     err = GetCollectionItemInfo(collection, tag, id, kCollectionDontWantIndex, kCollectionDontWantSize, &attributes);
  759.     if(!err){
  760.         attributes = attributes & ~kCollectionLockMask;                                    
  761.         err = SetCollectionItemInfo(collection, tag, id, kCollectionLockMask, attributes);
  762.     }
  763.     if(err == collectionItemNotFoundErr) err = noErr;        //If it wasn't there that's fine.
  764.     
  765.     return err;
  766. }
  767.